module kernel.arch.x86.idt;

const uint IDT_SIZE = 49;
const ubyte TASK_GATE = 0x1;
const ubyte INTERR_GATE = 0x02;
const ubyte TRAP_GATE = 0x03;

align(1) struct IDTgateOptions {
	ubyte Reserved0;
	uint bfflags;
	mixin(Bitfield!(bfflags,"Reserved1",2,"Reserved2",1,"SizeD",1,"Reserved3",1,"DPL",2,"Present",1));
}

struct IDTgate {
	ushort OffsetL;
	ushort Selector; /* Kernel segment */
	IDTgateOptions Options;
	ushort BaseH;
}

align(1) struct IDTPtr {
	ushort Limit;
	uint Base;
}

struct IDT {
	static:
	public:
		void init() {
			initIntTable();
			initIDTgates();
			setIDTR(cast(uint)&IDTgates,(IDT_SIZE * IDTgate.sizeof) - 1);
			volatile asm {
				sti;
			}
		}

		void initIntTable() {
			for (int i = 0; i < 20; i++)
				INTtable[i] = cast(void(*)())execeptionHandler;
			for (i = 20; i < IDT_SIZE; i++)
				INTtabe[i] = nullIntHandler;
		}

		void initIDTgates() {
			IDTgates[0] = makeIDTgate(ISR_0, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[1] = makeIDTgate(ISR_1, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[2] = makeIDTgate(ISR_2, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[3] = makeIDTgate(ISR_3, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[4] = makeIDTgate(ISR_4, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[5] = makeIDTgate(ISR_5, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[6] = makeIDTgate(ISR_6, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[7] = makeIDTgate(ISR_7, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[8] = makeIDTgate(ISR_8, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[9] = makeIDTgate(ISR_9, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[10] = makeIDTgate(ISR_10, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[11] = makeIDTgate(ISR_11, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[12] = makeIDTgate(ISR_12, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[13] = makeIDTgate(ISR_13, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[14] = makeIDTgate(ISR_14, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[15] = makeIDTgate(ISR_15, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */
			IDTgates[16] = makeIDTgate(ISR_16, KERNEL_MODE,INTERR_GATE); /*EXCEPTION */

			/* UNUSED */
			for(int i = 17; i <= 31; i++)
				IDTgates[i] = makeIDTgate(ISR_NULL, KERNEL_MODE,INTERR_GATE); /*RESERVED*/

			/* USER DEFINED */
			IDTgates[32] = makeIDTgate(ISR_32, KERNEL_MODE,INTERR_GATE); /*IRQ 0*/
			IDTgates[33] = makeIDTgate(ISR_33, KERNEL_MODE,INTERR_GATE); /*IRQ 1 (keyboard)*/
			IDTgates[34] = makeIDTgate(ISR_34, KERNEL_MODE,INTERR_GATE); /*IRQ 2*/
			IDTgates[35] = makeIDTgate(ISR_35, KERNEL_MODE,INTERR_GATE); /*IRQ 3*/
			IDTgates[36] = makeIDTgate(ISR_36, KERNEL_MODE,INTERR_GATE); /*IRQ 4*/
			IDTgates[37] = makeIDTgate(ISR_37, KERNEL_MODE,INTERR_GATE); /*IRQ 5*/
			IDTgates[38] = makeIDTgate(ISR_38, KERNEL_MODE,INTERR_GATE); /*IRQ 6*/
			IDTgates[39] = makeIDTgate(ISR_39, KERNEL_MODE,INTERR_GATE); /*IRQ 7*/
			IDTgates[40] = makeIDTgate(ISR_40, KERNEL_MODE,INTERR_GATE); /*IRQ 8*/
			IDTgates[41] = makeIDTgate(ISR_41, KERNEL_MODE,INTERR_GATE); /*IRQ 9*/
			IDTgates[42] = makeIDTgate(ISR_42, KERNEL_MODE,INTERR_GATE); /*IRQ 10*/
			IDTgates[43] = makeIDTgate(ISR_43, KERNEL_MODE,INTERR_GATE); /*IRQ 11*/
			IDTgates[44] = makeIDTgate(ISR_44, KERNEL_MODE,INTERR_GATE); /*IRQ 12*/
			IDTgates[45] = makeIDTgate(ISR_45, KERNEL_MODE,INTERR_GATE); /*IRQ 13*/
			IDTgates[46] = makeIDTgate(ISR_46, KERNEL_MODE,INTERR_GATE); /*IRQ 14*/
			IDTgates[47] = makeIDTgate(ISR_47, KERNEL_MODE,INTERR_GATE); /*IRQ 15*/
			IDTgates[48] = makeIDTgate(SYSCALL_ISR, USER_MODE, TRAP_GATE); /*SYSCALLs int 0x30*/
		}

		void setIDTR(uint base, ushort limit) {
			idtr.limit = limit;
			idtr.base = cast(uint)base;
			volatile asm {
				lidt idtr;
			}
		}

		IDTgate makeIDTgate(void (*func)(), int DPL, ushort type) {
			IDTgate tmp;
			ushort codeSeg = 0x08;
			volatile asm {
				movw CS,codeSeg;
			}
			tmp.OffsetL = ((cast(uint)func) & 0x0000FFFF); /* 0-15bit of the address */
			tmp.OffsetH = ((cast(uint)func) >> 16); /* 16-31 bit of the address */
			tmp.Selector = codesegment; /* use this selector */
			tmp.Options.Reserved0 = 0x00;
			tmp.Options.Reserved1 = type;
			tmp.Options.Reserved2 = 0x1;
			tmp.Options.Reserved3 = 0x0;
			tmp.Options.SizeD = 0x1;
			tmp.Options.DPL = DPL;
			tmp.Options.Present = 0x1;
		}

		void exceptionHandler(uint num, uint code) {
			char[] desc;
			switch(num) {
				case 0:
					desc = "Divide Error Exception (#DE)";
					break;
				case 1:
					desc = "Debug Exception (#DB)";
					break;
				case 2:
					desc = "NMI interrupt";
					break;
				case 3:
					desc = "Breakpoint Exception (#BP)";
					break;
				case 4:
					desc = "Overflow Exception (#OF)";
					break;
				case 5:
					desc = "Range Exceeded Exception (#BR)";
					break;
				case 6:
					desc = "Invalid Opcode Exception (#UD)";
					break;
				case 7:
					desc = "Device Not Avalilable Exception (#NM)";
					break;
				case 8:
					desc = "Double Fault Exception (#DF)";
					break;
				case 9:
					desc = "Coprocessor Segment Overrun";
					break;
				case 10:
					desc = "Invalid TSS Exception (#TS)";
					break;
				case 11:
					desc = "Segment Not Present (#NP)";
					break;
				case 12:
					desc = "Stack Fault Exception (#SS)";
					break;
				case 13:
					desc = "General Protection Exception (#GP)";
					break;
				case 14:
					uint request;
					desc = "Page Fault Exception (#PF)";
					/* the page fault source address*/
					asm {
						mov request,EAX;
					}
					/*__asm__("mov %%cr2,%%eax":"=a"(request));*/
					//on_page_fault(request, code); //TODO: Implement me!
					return;
				case 15:
					desc = "RESERVED";
					break;
				case 16:
					desc = "FPU Floating-Point Error (#MF)";
					break;
				case 17:
					desc = "Alignment Check Exception (#AC)";
					break;
				case 18:
					desc = "Machine Check Exception (#AC)";
					break;
				case 19:
					desc = "SIMD Floating Exception (#AC)";
					break;
				default:
					desc = "Unknown Exception";
					break;
			}

			if (!ON_STARTUP()) {
				//kprintfln!("Process pid={}: {}!\n",GET_CURRENT_TASK()->Info.pid,desc)();
				//do_exit(-1); //TODO: Implement me!
			} else{
				kprintfln!("{!fg:Red}KERNEL PANIC: {!fg:LightRed}Exception {x} caught: <{}>",num,desc);
				for(;;);
			}
			
			return;
		}

	private:
		IDTgate *IDTgates = cast(IDTgate)0x3000;

		void nullIntHandler() {
			kprintfln!("{!fg:Red}KERNEL PANIC: {!fg:LightRed}Null Interrupt Hit. System Halted.")();
			for (;;) {}
		}
}

private extern(C) IDTPtr idtr;
